﻿using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.AI;

namespace GameToolkit
{
    [ExecuteInEditMode]
    [AddComponentMenu("Navigation/NavMeshPrefab")]
    public class NavMeshPrefab : MonoBehaviour, INavMeshSource, ITransformSensor, ICullingTag
    {
        public enum ECollect
        {
            None,
            InChildren = 1,
            InParent = 2,
            InParentAndChildren = 3,
        }

#pragma warning disable CS0649
        [SerializeField]
        ECollect m_CollectionFrom = ECollect.InChildren;
        public ECollect CollectFrom { get { return m_CollectionFrom; } }

        [SerializeField]
        GameObject m_AdditionalRoot;
        public GameObject AdditionalCollectionRoot { get { return m_AdditionalRoot; } }

        [SerializeField]
        LayerMask m_BakeLayers = -1;
        public int Layers { get { return m_BakeLayers.value; } }

        [NavAgentType]
        [SerializeField]
        int m_AgentTypeID;
        public int AgentTypeID { get { return m_AgentTypeID; } }

        [SerializeField, NavAreaType]
        int m_AreaType;
        public int AreaType { get { return m_AreaType; } }

        [HideInInspector]
        [SerializeField]
        NavMeshData m_NavMeshData;

        [SerializeField, Tooltip("自动更新需要依赖 CullingBounds 系统")]
        bool m_AutoUpdate;

        //[Range(0, 0.1f)]
        //[SerializeField]
        //float m_TrackingPrecision = 0.01f;

        [SerializeField, HideInInspector]
        bool m_IsRuntimeModifier = true;

#pragma warning restore

        NavMeshData mNavMesh;
        NavMeshDataInstance mNavMeshInst;

        bool mIsStarted;
        VolumetricBounds mVolume;
        //TransformSensor.Sensor mSensor;
        bool mIsRuntimeModifier;

        public Bounds localBounds
        {
            get
            {
                if (mVolume != null)
                    return mVolume.localBounds;
                return new Bounds(Vector3.zero, Vector3.one);
            }
        }

        public Matrix4x4 localToWorldMatrix => transform.localToWorldMatrix;
        public Matrix4x4 worldToLocalMatrix => transform.worldToLocalMatrix;
        private void Start()
        {
            mIsStarted = true;
            UpdateNavMeshData();
            //if (m_AutoUpdate)
            //    mSensor = TransformSensor.CreateTransformSensor(transform, this);
        }

        private void OnEnable()
        {
            if (mVolume == null)
                mVolume = GetComponent<VolumetricBounds>();
            if (mIsStarted)
            {
                UpdateNavMeshData();
                //if (m_AutoUpdate)
                //    mSensor = TransformSensor.CreateTransformSensor(transform, this);
            }
            mIsRuntimeModifier = m_IsRuntimeModifier;
            if (mIsRuntimeModifier)
                RuntimeNavMeshBuilder.AddBuildSource(this);
        }

        private void OnDisable()
        {
            if (mIsRuntimeModifier)
                RuntimeNavMeshBuilder.RemoveBuildSource(this);
            //if (mSensor != null)
            //    mSensor.Release();
            if (mNavMeshInst.valid)
            {
                mNavMeshInst.Remove();
                mNavMeshInst = default(NavMeshDataInstance);
            }
            mNavMesh = null;
        }

        void ITransformSensor.OnTransformChanged(Transform trans, ETransformSensorFlag flags)
        {
            if (m_AutoUpdate)
            {
                UpdateNavMeshData();
                if (mIsRuntimeModifier)
                    RuntimeNavMeshBuilder.SetDirty(this);
            }
        }

        public bool AffectOnAgent(int agentId)
        {
            return true;
        }

        public void GetBuildSource(int areaType, ICollection<NavMeshBuildSource> sources)
        {
#if UNITY_EDITOR
            mVolume = GetComponent<VolumetricBounds>();
#endif
            Vector3 center, size;
            if (mVolume == null)
            {
                center = Vector3.zero;
                size = Vector3.one;
            }
            else
            {
                center = mVolume.m_BoundsCenter;
                size = mVolume.m_BoundsSize;
            }
            var mcenter = transform.TransformPoint(center);
            var scale = transform.lossyScale;
            var msize = new Vector3(size.x * Mathf.Abs(scale.x), size.y * Mathf.Abs(scale.y), size.z * Mathf.Abs(scale.z));

            var src = new NavMeshBuildSource();
            src.shape = NavMeshBuildSourceShape.ModifierBox;
            src.transform = Matrix4x4.TRS(mcenter, transform.rotation, Vector3.one);
            src.size = msize;
            // not walkable
            src.area = 1;
            sources.Add(src);
        }

        public void UpdateNavMeshData()
        {
            if (mNavMeshInst.valid)
            {
                mNavMeshInst.Remove();
                mNavMeshInst = default(NavMeshDataInstance);
            }
            if (isActiveAndEnabled && m_NavMeshData != null)
            {
                mNavMesh = m_NavMeshData;
                mNavMesh.position = transform.position;
                mNavMesh.rotation = transform.rotation;
                mNavMeshInst = NavMesh.AddNavMeshData(mNavMesh);
                mNavMeshInst.owner = this;
            }
        }

        //public void GetBuildSource(int agentId, ref List<NavMeshBuildSource> source)
        //{
        //    var mcenter = transform.TransformPoint(m_BoundsCenter);
        //    var scale = transform.lossyScale;
        //    var msize = new Vector3(m_BoundsSize.x * Mathf.Abs(scale.x), m_BoundsSize.y * Mathf.Abs(scale.y), m_BoundsSize.z * Mathf.Abs(scale.z));

        //    var src = new NavMeshBuildSource();
        //    src.shape = NavMeshBuildSourceShape.ModifierBox;
        //    src.transform = Matrix4x4.TRS(mcenter, transform.rotation, Vector3.one);
        //    src.size = msize;
        //    src.area = NavMesh.GetAreaFromName("Not Walkable");
        //    source.Add(src);
        //}

#if UNITY_EDITOR

        protected virtual void OnDrawGizmos()
        {
            Gizmos.DrawIcon(transform.position + Vector3.up * HandleUtility.GetHandleSize(transform.position) * 0.3f, "NavMeshData Icon");
        }

        private void OnValidate()
        {
            if (!GlobalUtil.IsPrefabStage(gameObject))
                UpdateNavMeshData();
        }

#endif
    }
}